package com.example.service;

import com.example.redisdemo.RedisLock;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author Jonathan
 * 2021/5/28 19:25
 * 多实例  选主执行定时任务 主挂了的话 其他实例跟上
 */
//@Service
public class RedisDemoService implements ApplicationRunner {


    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedisLock redisLock;
    @Resource
    private ServerProperties serverProperties;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        campaignForMasterRole();
    }

    /**
     * 该小弟任务是每18秒执行一次。【正常的业务】
     */
    private void doSomething() {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(() -> {
            System.out.println("doSomething……");
        }, 0, 18, TimeUnit.SECONDS);
    }

    /**
     * 该小弟任务是每29秒执行一次。【正常的业务】
     */
    private void doAnything() {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(() -> {
            System.out.println("doAnything……");
        }, 0, 29, TimeUnit.SECONDS);
    }

    /**
     * 服务启动的时候，最先抢到分布式锁的是master，每10秒去redis续命一下，redis里面是15秒过期
     * 如果master挂了，最先发现master挂了的升级为master
     */
    private void campaignForMasterRole() {
        String myAddress = serverProperties.getAddress().toString();
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(() -> {
            // 互斥的操作redis  原子性
            if (redisLock.tryLock("lock-1", 1, 1)) {
                try {
                    String masterAddress = (String) redisTemplate.opsForValue().get("master_address");
                    // 说明是首次竞选大哥
                    // redis"第一次"收到消息,准确的说是现在没有"master_address"这个键值对,
                    // 可能之前有挂了的，也可能是现在就是真正的第一次
                    // 还没有大哥  现在我当选大哥
                    if (masterAddress == null) {
                        redisTemplate.opsForValue().set("master_address", myAddress, 15, TimeUnit.SECONDS);
                        // 我是大哥 那么唤醒小弟
                        doSomething();
                        doAnything();
                    } else {
                        // 说明我已经是大哥了  续命一下就好。
                        if (masterAddress.equals(myAddress)) {
                            redisTemplate.opsForValue().set("master_address", myAddress, 15, TimeUnit.SECONDS);
                        }
                    }
                } finally {
                    redisLock.unlock("lock-1");
                }
            }
        }, 0, 10, TimeUnit.SECONDS);
    }
}
